# BabyOS Modbus主从机 最近开发者[Haimeng](https://gitee.com/chenhaimeng)发现解析从机回复的数据时出现Hardfault异常;其根因是代码实现没有考虑非对齐访问,导致M0内核的芯片会出现异常。于是对代码进行优化,并使用hc32l136和stm32l053两个M0的MCU进行验证。 下面介绍使用BabyOS时如何快速开发Modbus主从机。 ## 1 创建协议实例 首先创建协议实例,红色标记部分是用户需要实现提供给协议服务使用。 **协议名**:b_srv_protocol 根据协议名查找对应的解析和组包接口放在实例中。 **get_info**: 解析或者组包时需要向外获取的信息。 **cb**: 应用层处理解析结果的回调函数。 ![modbus_instance](../_static/modbus_instance.png) ## 2 Modbus主机 以modbus主机读取从机寄存器为例说明: ![modbus_instance](../_static/modbus_master.png) ## 3 Modbus从机 以从机被读寄存器为例说明: ![modbus_instance](../_static/modbus_slave.png) 从机不会主动发送数据,因此不需要组包接口,解析主机数据的同时将待回复数据组装完成。 开发者[ZHANGMIN](https://gitee.com/miniminiminini)增加了读写权限的想法,如果能从get_info获取到读写权限配置表,那么在解析到读或者写时都进行权限判断。 ## 4 需要注意的点 ### 4.1 modbus主机组包时,用于组包的信息从哪里传入? 这里table有两个作用,调用接口时存放待组包信息,接口执行完毕则是存放着组包结果。 ```SQL bModbusMasterRead_t *pread = (bModbusMasterRead_t *)table; pread->base_reg = 0; pread->reg_num = 2; pread->slave_addr = 1; len = bProtSrvPackage(modbus_protocol_id, B_MODBUS_CMD_READ_REG, table, sizeof(table)); ``` ### 4.2 modbus解析结果是什么数据结构? modbus解析结果数据结构统一如下所示: ```SQL typedef struct { uint8_t slave_id; uint8_t func_code; uint16_t base_reg; // Little endian uint16_t reg_num; // Little endian uint16_t *reg_value; // Little endian } bModbusCbParm_t; ``` ### 4.3 如何通过get_info获取寄存器信息 这里的buf,既可以作为从机地址存放的位置,也可以作为寄存器值的存放位置。 ```SQL int modbus_slave_get_info(bProtoInfoType_t type, uint8_t *buf, uint16_t buf_len) { int ret = -1; if (type == B_PROTO_INFO_MODBUS_REG_VALUE) { uint16_t reg_id = ((uint16_t *)buf)[0]; uint16_t tmp_value = g_slave_reg_value[reg_id]; b_log("r:%d %x\r\n", reg_id, tmp_value); ((uint16_t *)buf)[0] = tmp_value; ret = 0; } else if (type == B_PROTO_INFO_MODBUS_REG_PERMISSION) { bModbusPerm_t *perm = &g_slave_reg_perm; memcpy(buf, &perm, sizeof(perm)); ret = 0; } return ret; } ``` ### 4.4 如何创建读写权限表 创建空表:`MODBUS_PERM_CREATE_TABLE(g_slave_reg_perm);` 设置权限: ```SQL // 全部可读,但只有寄存器0可读写 for (int i = 0; i < MY_DEVICE_MODBUS_REG_NUM; i++) { MODBUS_PERM_CREATE_SET_STATE(&g_slave_reg_perm, i, MODBUS_PERM_READABLE); } MODBUS_PERM_CREATE_SET_STATE(&g_slave_reg_perm, 0, MODBUS_PERM_READWRITE); ``` 代码已经在主分支,欢迎各位开发者体验!